home *** CD-ROM | disk | FTP | other *** search
Text File | 1994-06-26 | 18.2 KB | 751 lines | [TEXT/MPS ] |
- /*
- File: Window.cp
-
- Contains: Implementation of TWindow, a base class which provides a
- framework for building way-cool windows which even John
- Sullivan would be happy with. Floating windows and “smart
- zooming” algorithms are based on code samples provided by
- Dean Yu.
-
- Written by: Dave Falkenburg
-
- Copyright: © 1993-94 by Dave Falkenburg, all rights reserved.
-
- Change History (most recent first):
-
- To Do: Create invisible, and bug free showing and hiding windows
- Window positioning methods (getters and setters)
- Display Manager support: AdjustWindowForNewScreen?
- Changes to support AEObject model
- */
-
- #include <Types.h>
- #include <Windows.h>
- #include <Errors.h>
- #include <Script.h> // for GetMBarHeight()
- #include <LowMem.h> // for LMGetWindowList()
-
- #include "AppLib.h"
- #include "Window.h"
- #include <Threads.h>
-
- const short kFloatingWindowKind = 1000;
- const short kNormalWindowKind = 1001;
- const WindowPtr kNoFloatingWindows = (WindowPtr) -1;
-
- const short kScreenEdgeSlop = 4;
- const short kSpaceForFinderIcons = 64;
- const short kMinimumTitleBarHeight = 21;
- const short kMinimumWindowSize = 32;
-
- static void HiliteShowHideFloatingWindows(Boolean hiliting,Boolean hiding);
-
- static void FindScreenRectWithLargestPartOfWindow(WindowPtr aWindow,Rect *theBestScreenRect, GDHandle * theBestDevice);
- static pascal void CalculateWindowAreaOnDevice(short depth,short deviceFlags,GDHandle targetDevice,long userData);
-
- struct CalcWindowAreaDeviceLoopUserData
- {
- GDHandle fScreenWithLargestPartOfWindow;
- long fLargestArea;
- Rect fWindowBounds;
- };
-
-
- TWindow::TWindow()
- {
- }
-
- TWindow::~TWindow()
- {
- }
-
- void
- TWindow::CreateWindow(Boolean isFloating)
- {
- WindowPtr behindWindow,oldFrontMostWindow;
-
- if (isFloating)
- {
- behindWindow = (WindowPtr) -1;
- oldFrontMostWindow = FrontWindow();
- }
- else
- {
- behindWindow = LastFloatingWindow();
-
- if (behindWindow == kNoFloatingWindows)
- oldFrontMostWindow = nil;
- else
- oldFrontMostWindow = (WindowPtr) ((WindowPeek) behindWindow)->nextWindow;
- }
-
- fWindow = MakeNewWindow(behindWindow);
-
- if (fWindow)
- {
- SetWRefCon(fWindow,(long) this);
-
- fIsFloatingWindow = isFloating;
- fIsVisible = true; // should be visibleFlag
-
- ((WindowPeek) fWindow)->windowKind = kNormalWindowKind;
-
- if (isFloating)
- {
- ((WindowPeek) fWindow)->windowKind = kFloatingWindowKind;
-
- // make sure the other window stays hilited
-
- if (oldFrontMostWindow)
- HiliteAndActivateWindow(oldFrontMostWindow,true);
- }
- else
- {
- ((WindowPeek) fWindow)->windowKind = kNormalWindowKind;
-
- // unhighlight the old front window
-
- if (oldFrontMostWindow)
- HiliteAndActivateWindow(oldFrontMostWindow,false);
-
- // hilite the new window…
-
- HiliteAndActivateWindow(fWindow,true);
- }
- }
- }
-
-
- void
- TWindow::AdjustCursor(EventRecord * /* anEvent */)
- {
- }
-
- void
- TWindow::Idle(EventRecord * /* anEvent */)
- {
- YieldToAnyThread();
- }
-
- void
- TWindow::Activate(Boolean /* activating */)
- {
- }
-
- void
- TWindow::Draw(void)
- {
- }
-
- void
- TWindow::Click(EventRecord * /* anEvent */)
- {
- }
-
- void
- TWindow::KeyDown(EventRecord * /* anEvent */)
- {
- }
-
-
- void
- TWindow::Select(void)
- {
- WindowPtr currentFrontWindow;
-
- if (fIsFloatingWindow)
- currentFrontWindow = FrontWindow();
- else
- currentFrontWindow = FrontNonFloatingWindow();
-
- if (currentFrontWindow != fWindow)
- {
- if (fIsFloatingWindow)
- BringToFront(fWindow);
- else
- {
- WindowPtr lastFloater = LastFloatingWindow();
-
- // If there are no floating windows,
- // just call SelectWindow like the good ol’ days
-
- if (lastFloater == kNoFloatingWindows)
- SelectWindow(fWindow);
- else
- {
- // Deactivate the window currently in front.
-
- HiliteAndActivateWindow(currentFrontWindow,false);
-
- // Bring it behind the last floating window and activate it.
- // Note that Inside Mac 1 states that you need to call PaintOne() and CalcVis() on a
- // window if you are using SendBehind() to bring it closer to the front. With System 7,
- // this is no longer necessary.
-
- SendBehind(fWindow,lastFloater);
- HiliteAndActivateWindow(fWindow,true);
- }
- }
- }
- }
-
-
- void
- TWindow::Drag(Point startPoint)
- {
- GrafPtr savePort;
- KeyMap theKeyMap;
- Boolean commandKeyDown = false;
- RgnHandle draggingRegion;
- long dragResult;
- WindowPeek windowAsWindowPeek = (WindowPeek) fWindow;
-
- if (WaitMouseUp()) // de-bounce?
- {
- // Set up the Window Manager port.
-
- GetPort(&savePort);
- SetPort(gWindowManagerPort);
- SetClip(GetGrayRgn());
-
- // Check to see if the command key is down.
-
- GetKeys(theKeyMap);
- commandKeyDown = ((theKeyMap[1] & 0x8000) != 0);
-
- if (commandKeyDown)
- {
- // We’re not going to change window ordering,
- // so make sure that we don’t drag in front of
- // other windows which may be in front of ours.
-
- ClipAbove(windowAsWindowPeek);
- }
- else if (!fIsFloatingWindow)
- {
- // We’re dragging a normal window, so make sure
- // that we don’t drag in front of any floating
- // windows.
-
- ClipAbove((WindowPeek) FrontNonFloatingWindow());
- }
-
- // Drag an outline of the window around the desktop.
- // NOTE: DragGrayRgn destroys the region passed in, so make a copy
-
- draggingRegion = NewRgn();
- CopyRgn(windowAsWindowPeek->strucRgn,draggingRegion);
- dragResult = DragGrayRgn(draggingRegion, startPoint, &gDeskRectangle, &gDeskRectangle, noConstraint, nil);
- DisposeRgn(draggingRegion);
-
-
- SetPort(savePort); // Get back to old port
-
- if ((dragResult != 0) && (dragResult != 0x80008000))
- {
- short newHorizontalPosition,newVerticalPosition;
-
- newHorizontalPosition = (short) (**windowAsWindowPeek->contRgn).rgnBBox.left + (dragResult & 0xFFFF);
- newVerticalPosition = (short) (**windowAsWindowPeek->contRgn).rgnBBox.top + (dragResult >> 16);
-
- MoveWindow(fWindow,newHorizontalPosition,newVerticalPosition,false);
- }
- }
-
- if (!commandKeyDown)
- Select();
- }
-
-
- void
- TWindow::Grow(Point startPoint)
- {
- GrafPtr oldPort;
- long newSize;
- Rect oldWindowRect,resizeLimits;
-
- GetPort(&oldPort);
-
- GetWindowSizeLimits(&resizeLimits);
- newSize = GrowWindow(fWindow,startPoint,&resizeLimits);
- if (newSize)
- {
- oldWindowRect = fWindow->portRect;
- SizeWindow(fWindow,(short) newSize,(short) (newSize >> 16),true);
- SetPort(fWindow);
- AdjustForNewWindowSize(&oldWindowRect,&fWindow->portRect);
- }
-
- SetPort(oldPort);
- }
-
-
- void
- TWindow::Zoom(short zoomState)
- {
- GrafPtr oldPort;
- FontInfo systemFontInfo;
- short titleBarHeight;
- Rect bestScreenRect,perfectWindowRect,scratchRect;
- short amountOffscreen;
- WindowPeek windowAsWindowPeek = (WindowPeek) fWindow;
- GDHandle bestDevice;
-
- GetPort(&oldPort);
-
- // Figure out the height of the title bar so we can properly position
- // a window. The algorithm is stolen from the System 7.x 'WDEF' (0)
- //
- // This probably isn’t the best thing to do: A better way might be
- // to diff the structure and content region rectangles?
-
- SetPort(gWindowManagerPort);
- GetFontInfo(&systemFontInfo);
- titleBarHeight = (short) (systemFontInfo.ascent + systemFontInfo.descent + 4);
- if ((titleBarHeight % 2) == 1)
- titleBarHeight--;
- if (titleBarHeight < kMinimumTitleBarHeight)
- titleBarHeight = kMinimumTitleBarHeight;
-
-
- // Only do the voodoo magic if we are really “zooming” the window.
-
- if (zoomState == inZoomOut)
- {
- FindScreenRectWithLargestPartOfWindow(fWindow,&bestScreenRect,&bestDevice);
- bestScreenRect.top += titleBarHeight;
-
- GetPerfectWindowSize(&perfectWindowRect);
- OffsetRect(&perfectWindowRect,-perfectWindowRect.left,-perfectWindowRect.top);
-
- // Take the zero-pined perfect window size and move it to
- // the top left of the window’s content region.
-
- OffsetRect(&perfectWindowRect,(**windowAsWindowPeek->contRgn).rgnBBox.left,
- (**windowAsWindowPeek->contRgn).rgnBBox.top);
-
-
- // Does perfectWindowRect fit completely on the best screen?
-
- SectRect(&perfectWindowRect, &bestScreenRect, &scratchRect);
- if (!EqualRect(&perfectWindowRect, &scratchRect))
- {
- // SectRect sez perfectWindowRect doesn’t completely fit
- // on the screen, so bump the window so that more of it fits.
-
- // Make sure that the left edge of perfectWindowRect is forced
- // onto the best screen. This is in case we are bumping
- // the window to the right.
-
- amountOffscreen = bestScreenRect.left - perfectWindowRect.left;
- if (amountOffscreen > 0)
- {
- perfectWindowRect.left += amountOffscreen;
- perfectWindowRect.right += amountOffscreen;
- }
-
- // Make sure that the left edge of perfectWindowRect is forced
- // onto the best screen. This is in case we are bumping
- // the window downward to a new screen.
-
- amountOffscreen = bestScreenRect.top - perfectWindowRect.top;
- if (amountOffscreen > 0)
- {
- perfectWindowRect.top += amountOffscreen;
- perfectWindowRect.bottom += amountOffscreen;
- }
-
- // If right edge of window falls off the screen,
- // Move window to the left until the right edge IS on the screen
- // OR the left edge is at bestScreenRect.left
-
- amountOffscreen = perfectWindowRect.right - bestScreenRect.right;
- if (amountOffscreen > 0)
- {
- // Are we going to push the left edge offscreen? If so, change the
- // offset so we move the window all the way over to the left.
-
- if ((perfectWindowRect.left - amountOffscreen) < bestScreenRect.left)
- amountOffscreen = perfectWindowRect.left - bestScreenRect.left;
-
- perfectWindowRect.left -= amountOffscreen;
- perfectWindowRect.right -= amountOffscreen;
- }
-
- // If bottom edge of window falls off the screen,
- // Move window to up until the bottom edge IS on the screen
- // OR the top edge is at bestScreenRect.top
-
- amountOffscreen = perfectWindowRect.bottom - bestScreenRect.bottom;
- if (amountOffscreen > 0)
- {
- // Are we going to push the top edge offscreen? If so, change the
- // offset so we move the window just to the top.
-
- if ((perfectWindowRect.top - amountOffscreen) < bestScreenRect.top)
- amountOffscreen = perfectWindowRect.top - bestScreenRect.top;
-
- perfectWindowRect.top -= amountOffscreen;
- perfectWindowRect.bottom -= amountOffscreen;
- }
-
- SectRect(&perfectWindowRect, &bestScreenRect, &scratchRect);
- if (!EqualRect(&perfectWindowRect, &scratchRect))
- {
- // The edges of the window still fall offscreen,
- // so make the window smaller until it fits.
-
- if (perfectWindowRect.bottom > bestScreenRect.bottom)
- perfectWindowRect.bottom = bestScreenRect.bottom;
-
- // If the right edge is still falling off,
- // save space for Finder’s disk icons as well.
-
- if (perfectWindowRect.right > bestScreenRect.right)
- {
- perfectWindowRect.right = bestScreenRect.right;
-
- // If we were on the main screen, leave room for Finder icons, too.
-
- if (bestDevice == GetMainDevice())
- perfectWindowRect.right -= kSpaceForFinderIcons;
- }
- }
- }
-
- // Stash our new rectangle inside of the Window’s dataHandle
- // so that ZoomWindow does the right thing.
-
- (**((WStateDataHandle) (windowAsWindowPeek->dataHandle))).stdState = perfectWindowRect;
- }
-
- // HEY YOU! Don’t forget to set the port to the window being zoomed
- // Why, you ask? Because IM-IV-50 says to; otherwise you die
-
- SetPort(fWindow);
-
- Rect oldWindowRect = fWindow->portRect;
-
- ZoomWindow(fWindow,zoomState,false);
- AdjustForNewWindowSize(&oldWindowRect,&fWindow->portRect);
-
- SetPort(oldPort);
- }
-
-
- void
- TWindow::ShowOrHide(Boolean showFlag)
- {
- // This wrapper for ShowHide allows us to correctly
- // show or hide floating windows behind the back of the
- // rest of the application.
-
- fIsVisible = showFlag;
- ShowHide(fWindow,showFlag);
- }
-
-
- Boolean
- TWindow::EventFilter(EventRecord * /* theEvent */)
- {
- return (false);
- }
-
-
- void
- TWindow::GetPerfectWindowSize(Rect *perfectSize)
- {
- *perfectSize = qd.screenBits.bounds;
- }
-
- void
- TWindow::GetWindowSizeLimits(Rect *limits)
- {
- limits->top = limits->left = kMinimumWindowSize;
- limits->right = gDeskRectangle.right - gDeskRectangle.left;
- limits->bottom = gDeskRectangle.bottom - gDeskRectangle.top;
- }
-
- void
- TWindow::AdjustForNewWindowSize(Rect * /* oldRect */, Rect * /* newSize */)
- {
- }
-
-
- Boolean
- TWindow::IsVisible(void)
- {
- return fIsVisible;
- }
-
-
- Boolean
- TWindow::CanClose(void)
- {
- return(true);
- }
-
- Boolean
- TWindow::Close(void)
- {
- WindowPtr newFrontWindow = nil;
-
- if (FrontNonFloatingWindow() == fWindow)
- newFrontWindow = (WindowPtr) ((WindowPeek) fWindow)->nextWindow;
-
- DisposeWindow(fWindow);
-
- if (newFrontWindow)
- HiliteAndActivateWindow(newFrontWindow,true);
- return(true);
- }
-
-
- Boolean
- TWindow::DeleteAfterClose(void)
- {
- return(true);
- }
-
- Boolean
- TWindow::CanEdit(void)
- {
- return(false);
- }
-
- void
- TWindow::DoEditMenu(short /* menuCode */)
- {
- }
-
-
- OSErr
- TWindow::HandleDrag(DragTrackingMessage /* dragMessage */, DragReference /* theDrag */)
- {
- return(noErr);
- }
-
-
- OSErr
- TWindow::HandleDrop(DragReference /* theDrag */)
- {
- return(noErr);
- }
-
-
- ///////////////////////////////////////////////////////////////////////////
- //
- // Utility Functions used for floating windows
- //
-
- TWindow *
- GetWindowObject(WindowPtr aWindow)
- {
- short wKind;
-
- if (aWindow != nil)
- {
- wKind = ((WindowPeek) aWindow)->windowKind;
-
- if (wKind >= userKind)
- {
- // All windowKinds >= userKind are based upon TWindow
-
- return (TWindow *) GetWRefCon(aWindow);
- }
- }
- return (TWindow *) nil;
- }
-
-
- WindowPtr
- LastFloatingWindow(void)
- {
- WindowPeek aWindow = (WindowPeek) FrontWindow();
- WindowPtr lastFloater = (WindowPtr) kNoFloatingWindows;
-
- while (aWindow && (aWindow->windowKind == kFloatingWindowKind))
- {
- if (aWindow->visible)
- lastFloater = (WindowPtr) aWindow;
-
- aWindow = (WindowPeek) aWindow->nextWindow;
- }
- return(lastFloater);
- }
-
-
- WindowPtr
- FrontNonFloatingWindow(void)
- {
- WindowPeek aWindow = (WindowPeek) LMGetWindowList();
-
- // Skip over floating windows
-
- while (aWindow && (aWindow->windowKind == kFloatingWindowKind))
- aWindow = (WindowPeek) aWindow->nextWindow;
-
- // Skip over invisible, but otherwise normal windows
-
- while (aWindow && (aWindow->visible == 0))
- aWindow = (WindowPeek) aWindow->nextWindow;
-
- return (WindowPtr) aWindow;
- }
-
-
- void
- HiliteAndActivateWindow(WindowPtr aWindow,Boolean active)
- {
- GrafPtr oldPort;
- TWindow * wobj = GetWindowObject(aWindow);
-
- if (aWindow)
- {
- HiliteWindow(aWindow,active);
-
- if (wobj != nil)
- {
- GetPort(&oldPort);
- SetPort(aWindow);
- wobj->Activate(active);
- SetPort(oldPort);
- }
- }
- }
-
- void
- SuspendResumeWindows(Boolean resuming)
- {
- // When we suspend/resume, hide/show all the visible floaters
-
- HiliteShowHideFloatingWindows(resuming,true);
- }
-
- void
- HiliteWindowsForModalDialog(Boolean hiliting)
- {
- // When we display a modal dialog, we need to unhighlight
- // all visible floaters. We also need to re-hilite them
- // afterwards.
-
- HiliteShowHideFloatingWindows(hiliting,false);
- }
-
- void
- HiliteShowHideFloatingWindows(Boolean hiliting,Boolean dohiding)
- {
- WindowPeek aWindow;
- TWindow * wobj;
-
- HiliteAndActivateWindow(FrontNonFloatingWindow(),hiliting);
-
- aWindow = LMGetWindowList();
- while (aWindow && aWindow->windowKind == kFloatingWindowKind)
- {
- wobj = GetWindowObject((WindowPtr) aWindow);
-
- // If we are hiding or showing, only hide/show windows
- // that were visible to begin with.
-
- if (dohiding && (wobj != nil) && ((wobj->IsVisible()) || (!hiliting)))
- ShowHide((WindowPtr) aWindow,hiliting);
-
- // All floaters are hilited if any floater is hilited
-
- HiliteWindow((WindowPtr) aWindow,hiliting);
- aWindow = (WindowPeek) aWindow->nextWindow;
- }
- }
-
-
- ///////////////////////////////////////////////////////////////////////////
- //
- // Routines used for dealing with windows and multiple screens
- //
-
- pascal void
- CalculateWindowAreaOnDevice(short /* depth */,short /* deviceFlags */,GDHandle targetDevice,long userData)
- {
- CalcWindowAreaDeviceLoopUserData * deviceLoopDataPtr;
- long windowAreaOnThisScreen;
- Rect windowRectOnThisScreen;
-
- deviceLoopDataPtr = (CalcWindowAreaDeviceLoopUserData *) userData;
-
- SectRect(&deviceLoopDataPtr->fWindowBounds, &(**targetDevice).gdRect,&windowRectOnThisScreen);
- OffsetRect(&windowRectOnThisScreen,-windowRectOnThisScreen.left,-windowRectOnThisScreen.top);
- windowAreaOnThisScreen = windowRectOnThisScreen.right * windowRectOnThisScreen.bottom;
-
- if (windowAreaOnThisScreen > deviceLoopDataPtr->fLargestArea)
- {
- deviceLoopDataPtr->fLargestArea = windowAreaOnThisScreen;
- deviceLoopDataPtr->fScreenWithLargestPartOfWindow = targetDevice;
- }
- }
-
-
- DeviceLoopDrawingUPP CallCalcWindowAreaOnDevice = NewDeviceLoopDrawingProc(&CalculateWindowAreaOnDevice);
-
-
- void
- FindScreenRectWithLargestPartOfWindow(WindowPtr aWindow,Rect *theBestScreenRect,GDHandle * theBestDevice)
- {
- RgnHandle copyOfWindowStrucRgn;
- CalcWindowAreaDeviceLoopUserData deviceLoopData;
-
- // Use DeviceLoop to find out what GDevice contains the largest
- // portion of the supplied window.
- //
- // NOTE: Assumes thePort == the Window Manager Port because we using
- // the window strucRgn, not contRgn.
-
- deviceLoopData.fScreenWithLargestPartOfWindow = nil;
- deviceLoopData.fLargestArea = -1;
- deviceLoopData.fWindowBounds = (**(((WindowPeek) aWindow)->contRgn)).rgnBBox;
-
- copyOfWindowStrucRgn = NewRgn();
- CopyRgn(((WindowPeek) aWindow)->strucRgn,copyOfWindowStrucRgn);
-
- DeviceLoop(copyOfWindowStrucRgn,CallCalcWindowAreaOnDevice,(long) &deviceLoopData,singleDevices);
-
- DisposeRgn(copyOfWindowStrucRgn);
-
- *theBestDevice = deviceLoopData.fScreenWithLargestPartOfWindow;
- *theBestScreenRect = (**(deviceLoopData.fScreenWithLargestPartOfWindow)).gdRect;
-
- // Leave some space around the edges of the screen so window look good, AND
- // if the best device is the main screen, leave space for the Menubar
-
- InsetRect(theBestScreenRect,kScreenEdgeSlop,kScreenEdgeSlop);
- if (GetMainDevice() == deviceLoopData.fScreenWithLargestPartOfWindow)
- theBestScreenRect->top += GetMBarHeight();
- }
-
-
- ///////////////////////////////////////////////////////////////////////////
- //
- // Drag Manager callback routines which dispatch to a window’s method
- //
-
- pascal OSErr CallWindowDragTrackingHandler(DragTrackingMessage dragMessage,WindowPtr theWindow,void * /* refCon */,DragReference theDrag)
- {
- TWindow *wobj = GetWindowObject(theWindow);
-
- if (wobj)
- return(wobj->HandleDrag(dragMessage,theDrag));
- else
- return(noErr);
- }
-
-
- pascal OSErr CallWindowDragReceiveHandler(WindowPtr theWindow,void * /* refCon */,DragReference theDrag)
- {
- TWindow *wobj = GetWindowObject(theWindow);
-
- if (wobj)
- return(wobj->HandleDrop(theDrag));
- else
- return(noErr);
- }
-